package file

import (
	"context"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/ledongthuc/pdf"
	"github.com/xuri/excelize/v2"
	"golang.org/x/sync/semaphore"
)

const (
	HOST           = "https://syapi.sytechnology.com"
	TOKEN          = "35a96fe3-7f1c-4645-bf76-f82c0283f21b"
	FileName       = "营业执照.xlsx"
	UpdateFileName = "file7_update.xlsx"
	SheetName      = "个体户"
	HeaderRowIndex = 1
	MaxRetry       = 3
	Concurrency    = 5
)

type Record struct {
	FilePhysicalID string
	FileName       string
}

type ExcelInfo struct {
	FileId string // 文件物理ID
	FileNo string // 文件编号
	Text   string // 提取的文本
}

func ReadExcel() []Record {
	f, err := excelize.OpenFile("../" + FileName)
	if err != nil {
		log.Fatalf("无法打开文件: %v", err)
	}
	defer f.Close()

	rows, err := f.GetRows(SheetName)
	if err != nil {
		log.Fatalf("无法获取行: %v", err)
	}

	colIndices := map[string]int{
		"文件物理ID": -1,
		"文件名称":   -1,
	}
	for colIndex, colCell := range rows[HeaderRowIndex-1] {
		if _, ok := colIndices[colCell]; ok {
			colIndices[colCell] = colIndex
		}
	}

	var records []Record
	for _, row := range rows[HeaderRowIndex:] {
		filePhysicalID := getCellValue(row, colIndices["文件物理ID"])
		if filePhysicalID != "" {
			record := Record{
				FilePhysicalID: filePhysicalID,
				FileName:       getCellValue(row, colIndices["文件名称"]),
			}
			records = append(records, record)
		}
	}

	return records
}

func cleanString(str string) string {
	return strings.TrimSpace(str)
}

func WriteFile(list []Record, wg *sync.WaitGroup) {
	sem := semaphore.NewWeighted(Concurrency)
	outputDir := "../pdf"

	if err := os.MkdirAll(outputDir, os.ModePerm); err != nil {
		fmt.Println("Error creating directories:", err)
		return
	}

	for _, item := range list {
		wg.Add(1)

		if err := sem.Acquire(context.Background(), 1); err != nil {
			fmt.Println("Failed to acquire semaphore:", err)
			wg.Done()
			continue
		}

		go func(item Record) {
			defer wg.Done()
			defer sem.Release(1)

			cleanedFilePhysicalID := strings.ReplaceAll(item.FilePhysicalID, "\t", "")
			outputFilename := cleanedFilePhysicalID + ".unknown" // Temporary extension

			outputPath := filepath.Join(outputDir, outputFilename)

			if fileExistsAndNotEmpty(outputPath) {
				fmt.Printf("File %s already exists and is not empty, skipping download.\n", outputPath)
				return
			}

			downloadFile := func(url, outputPath string) error {
				client := &http.Client{}

				req, err := http.NewRequest("GET", url, nil)
				if err != nil {
					return fmt.Errorf("error while creating request: %v", err)
				}
				req.Header.Set("Connection", "keep-alive")
				req.Header.Set("Upgrade-Insecure-Requests", "1")

				resp, err := client.Do(req)
				if err != nil {
					return fmt.Errorf("error while downloading file: %v", err)
				}
				defer resp.Body.Close()

				if resp.StatusCode != http.StatusOK {
					return fmt.Errorf("server returned non-200 status: %d %s", resp.StatusCode, resp.Status)
				}

				data, err := io.ReadAll(resp.Body)
				if err != nil {
					return fmt.Errorf("error reading response body: %v", err)
				}

				// Detect the MIME type and derive the file extension
				mimeType := http.DetectContentType(data)
				extensions, _ := mime.ExtensionsByType(mimeType)
				actualExtension := ".bin" // Default binary extension if no other match found
				if mimeType == "image/jpeg" {
					actualExtension = ".jpg" // Set .jpg for JPEG explicitly
				} else if len(extensions) > 0 {
					actualExtension = extensions[0]
				}

				// Update the output path with the correct extension
				outputPath = strings.TrimSuffix(outputPath, ".unknown") + actualExtension

				if err = os.WriteFile(outputPath, data, 0644); err != nil {
					return fmt.Errorf("error while writing file: %v", err)
				}
				return nil
			}

			var err error
			for i := 0; i < MaxRetry; i++ {
				filePhysicalID := cleanString(item.FilePhysicalID)
				url := fmt.Sprintf("%s/file/getFile/%s?access_token=%s", HOST, filePhysicalID, TOKEN)

				err = downloadFile(url, outputPath)
				if err == nil && fileExistsAndNotEmpty(outputPath) {
					break
				}

				fmt.Printf("Failed to download file %s, retrying... (%d/%d)\n", outputPath, i+1, MaxRetry)
				time.Sleep(1 * time.Second)
			}

			if err != nil {
				fmt.Printf("Failed to download file %s after multiple attempts: %v\n", item.FilePhysicalID, err)
			} else {
				fmt.Printf("File %s downloaded successfully to %s\n", item.FilePhysicalID, outputPath)
			}
		}(item)
	}
}

func getCellValue(row []string, index int) string {
	if index >= 0 && index < len(row) {
		return row[index]
	}
	return ""
}

func fileExistsAndNotEmpty(filePath string) bool {
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		return false
	}
	return fileInfo.Size() > 0
}

func ParsePDF(list *[]Record, wg *sync.WaitGroup) []ExcelInfo {
	var excelList []ExcelInfo
	var mutex sync.Mutex // 创建一个互斥锁
	semaphore := make(chan struct{}, 10)

	for _, fileInfo := range *list {
		wg.Add(1)
		go func(fileId string) {
			defer wg.Done()
			defer func() {
				if r := recover(); r != nil {
					fmt.Println("Recovered from panic:", r, "FileID:", fileId)
				}
				<-semaphore // 无论如何都释放信号量的令牌
			}()
			semaphore <- struct{}{} // 获取信号量的令牌

			cleanedFilePhysicalID := strings.ReplaceAll(fileId, "\t", "")
			outputFilename := cleanedFilePhysicalID + ".pdf"

			filePath := fmt.Sprintf("../pdf/%s", outputFilename)
			f, r, err := pdf.Open(filePath)
			if err != nil {
				fmt.Println("Error opening PDF file:", err, fileId)
				return
			}
			defer f.Close()

			var textBuilder strings.Builder
			b, err := r.GetPlainText()
			if err != nil {
				fmt.Println("Error getting plain text from PDF:", err)
				return
			}

			if _, err := io.Copy(&textBuilder, b); err != nil {
				fmt.Println("Error reading text from PDF:", err)
				return
			}
			text := textBuilder.String()

			re := regexp.MustCompile(`编号为\s*(.*?)\s*的`)
			matches := re.FindStringSubmatch(text)

			mutex.Lock() // 在修改excelList之前加锁
			if len(matches) > 1 {
				excelList = append(excelList, ExcelInfo{
					FileId: fileId,
					FileNo: matches[1],
					Text:   text,
				})
			}

			mutex.Unlock() // 修改完成后解锁
		}(fileInfo.FilePhysicalID)
	}
	wg.Wait()
	return excelList
}

func UpdateExcel(list []ExcelInfo) {
	f, err := excelize.OpenFile("../" + FileName)
	if err != nil {
		fmt.Println("Error opening Excel file:", err)
		return
	}
	defer func() {
		if err := f.Close(); err != nil {
			fmt.Println("Error closing Excel file:", err)
		}
	}()

	// 获取工作表中所有的行
	rows, err := f.GetRows(SheetName)
	if err != nil {
		fmt.Println("Error reading rows:", err)
		return
	}

	// 假设第二行是标题行，找出各个关键列的索引
	titleRow := rows[HeaderRowIndex-1]
	idColIndex, fileNoColIndex := -1, -1
	for col, cell := range titleRow {
		switch cell {
		case "文件物理ID":
			idColIndex = col
		case "保理服务合同编号":
			fileNoColIndex = col
		}
	}

	// 检查是否所有需要的列都找到了
	if idColIndex == -1 || fileNoColIndex == -1 {
		fmt.Println("Error: One or more required columns not found")
		return
	}

	// 遍历list并更新数据
	for _, info := range list {
		for rowIndex, row := range rows {
			if rowIndex < HeaderRowIndex { // 跳过前两行（第一行和标题行）
				continue
			}
			if len(row) > idColIndex && row[idColIndex] == info.FileId {
				// 计算单元格地址并更新值
				// fileNoColIndex + 1 是 "保理服务合同编号" 当前列的地址
				// 所以 +2 表示下一个列
				nextColIndex := fileNoColIndex + 1 + 1
				fileNoCell, _ := excelize.CoordinatesToCellName(nextColIndex, rowIndex+1)

				f.SetCellValue(SheetName, fileNoCell, info.FileNo)
				break
			}
		}
	}

	// 保存文件
	if err := f.SaveAs("../" + UpdateFileName); err != nil {
		fmt.Println("Error saving Excel file:", err)
	}
}
